﻿using HtmlAgilityPack;
using ProjectFootball.MVC.Foundation;
using ProjectFootball.MVC.Foundation.ResourceLibrary;
using ProjectFootball.MVC.Models.EntityFramework.Entities;
using ProjectFootball.MVC.Models.RepositoryProviders;
using System;
using System.Linq;
using System.Security.Principal;

namespace ProjectFootball.MVC.Services.Import
{
    public class Espn : BaseImport
    {
        public Espn(IRepositoryProvider provider, IPrincipal user)
            : base(ImportSite.Espn, provider, user)
        {
        }

        private LookupCompetition lookupCompetition;

        public void ImportMatches(DateTime startDate, DateTime endDate, Guid competitionGuid)
        {
            lookupCompetition = GetLookupCompetition(competitionGuid);

            if (lookupCompetition == null)
                return;

            Uri uri;

            DateTime searchDate = DateTime.Now;

            while (searchDate <= endDate)
            {
                uri = new Uri(string.Format("http://soccernet.espn.go.com/fixtures/_/league/{0}/date/{1}?cc=5739", lookupCompetition.SiteCompetitionId, searchDate.ToString("yyyyMMdd")));
                ImportFixtures(uri, startDate, endDate);
                searchDate = searchDate.AddDays(29);
            }

            searchDate = startDate.AddDays(28);

            while (searchDate <= DateTime.Now)
            {
                uri = new Uri(string.Format("http://soccernet.espn.go.com/results/_/league/{0}/date/{1}?cc=5739", lookupCompetition.SiteCompetitionId, searchDate.ToString("yyyyMMdd")));
                ImportResults(uri, startDate, endDate);
                searchDate = searchDate.AddDays(29);
            }

            uri = new Uri(string.Format("http://soccernet.espn.go.com/results/_/league/{0}/date/{1}?cc=5739", lookupCompetition.SiteCompetitionId, endDate.ToString("yyyyMMdd")));
            ImportResults(uri, startDate, endDate);
        }

        private void ImportFixtures(Uri uri, DateTime startDate, DateTime endDate)
        {
            HtmlDocument document = GetHtmlDocument(uri);
            HtmlNodeCollection matches = document.DocumentNode.SelectNodes("//table[@class=\"tablehead\"][tr/td[@colspan=\"7\" or @colspan=\"6\"]]");

            bool isPremierLeague = uri.AbsolutePath.IndexOf("eng.1") > 0;

            foreach (HtmlNode table in matches)
            {
                string matchDateString = isPremierLeague ? table.SelectSingleNode("tr/td[@colspan=\"7\"]").InnerText : table.SelectSingleNode("tr/td[@colspan=\"6\"]").InnerText;

                DateTime matchDate;
                if (!DateTime.TryParse(matchDateString, out matchDate))
                    continue;

                if (!matchDate._IsBetween(startDate, endDate))
                    continue;

                foreach (HtmlNode row in table.SelectNodes("tr[@class!=\"stathead\"][@class!=\"colhead\"]"))
                {
                    string team1EspnId = string.Empty;
                    string team1Name = string.Empty;
                    string team2EspnId = string.Empty;
                    string team2Name = string.Empty;
                    string espnMatchId = string.Empty;
                    string matchURL = string.Empty;
                    Guid team1Guid = Guid.Empty;
                    Guid team2Guid = Guid.Empty;

                    bool isValidMatch = true;

                    for (int cellCount = 0; cellCount <= row.SelectNodes("td").Count - 1 && isValidMatch; cellCount++)
                    {
                        HtmlNode cell = row.SelectNodes("td")[cellCount];
                        switch (cellCount)
                        {
                            case 0:
                                DateTime time;
                                if (cell.InnerText.Length < 5 || !DateTime.TryParse(cell.InnerText.Substring(0, 5).Trim(), out time))
                                {
                                    isValidMatch = false;
                                    break;
                                }

                                matchDate = matchDate.AddHours(time.Hour).AddMinutes(time.Minute);
                                break;

                            case 1:
                                if (cell.SelectSingleNode("a") != null)
                                {
                                    team1Name = cell.InnerText;
                                    HtmlAttribute clubURL = cell.SelectSingleNode("a").Attributes["href"];
                                    team1EspnId = clubURL.Value.GetTextBetween("team/_/id/", "/");
                                    team1Guid = ProcessTeam(team1EspnId, team1Name, (Guid)lookupCompetition.Competition.CountryGuid);
                                }
                                break;

                            case 2:
                                matchURL = cell.SelectSingleNode("a").Attributes["href"].Value;

                                if (matchURL.IndexOf("/uk/en/report/") > -1)
                                    espnMatchId = matchURL.GetTextBetween("/uk/en/report/", "/report.html");
                                else if (matchURL.IndexOf("/statistics/id/") > -1)
                                    espnMatchId = matchURL.GetTextBetween("/statistics/id/", "/statistics.html");
                                else if (matchURL.IndexOf("/uk/en/gamecast/") > -1)
                                    espnMatchId = matchURL.GetTextBetween("/uk/en/gamecast/", "/gamecast.html");

                                break;

                            case 3:
                                if (cell.SelectSingleNode("a") != null)
                                {
                                    team2Name = cell.InnerText;
                                    HtmlAttribute clubURL = cell.SelectSingleNode("a").Attributes["href"];
                                    team2EspnId = clubURL.Value.GetTextBetween("team/_/id/", "/");
                                    team2Guid = ProcessTeam(team2EspnId, team2Name, (Guid)lookupCompetition.Competition.CountryGuid);
                                }
                                continue;
                        }
                    }

                    if (isValidMatch)
                        ProcessMatch(espnMatchId, ImportStatus.Fixture, matchDate, null, lookupCompetition.CompetitionGuid, team1Guid, null, null, team2Guid, null, null);
                }
            }
        }

        private void ImportResults(Uri uri, DateTime startDate, DateTime endDate)
        {
            HtmlDocument document = GetHtmlDocument(uri);
            HtmlNodeCollection matches = document.DocumentNode.SelectNodes("//table[@class=\"tablehead\"][tr/td[@colspan=\"7\" or @colspan=\"6\"]]");

            bool isPremierLeague = uri.AbsolutePath.IndexOf("eng.1") > 0;

            if (matches != null)
            {
                foreach (HtmlNode table in matches)
                {
                    String matchDateString = isPremierLeague ? table.SelectSingleNode("tr/td[@colspan=\"7\"]").InnerText : table.SelectSingleNode("tr/td[@colspan=\"6\"]").InnerText;
                    DateTime matchDate = Convert.ToDateTime(matchDateString);

                    if (matchDate._IsBetween(startDate, endDate))
                    {
                        foreach (HtmlNode row in table.SelectNodes("tr[@class!=\"stathead\"][@class!=\"colhead\"]"))
                        {
                            bool fullTime = false;
                            string team1EspnId = string.Empty;
                            string team1Name = string.Empty;
                            string team2EspnId = string.Empty;
                            string team2Name = string.Empty;
                            string espnMatchId = string.Empty;
                            string matchURL = string.Empty;
                            Guid team1Guid = Guid.Empty;
                            Guid team2Guid = Guid.Empty;
                            short team1FT = 0;
                            short team2FT = 0;

                            for (Int32 cellCount = 0; cellCount <= row.SelectNodes("td").Count - 1; cellCount++)
                            {
                                HtmlNode cell = row.SelectNodes("td")[cellCount];
                                switch (cellCount)
                                {
                                    case 0:
                                        fullTime = (cell.InnerText == "FT" || cell.InnerText == "FT-Pens" || cell.InnerText == "AET");
                                        break;

                                    case 1:
                                        if (cell.SelectSingleNode("a") != null)
                                        {
                                            team1Name = cell.InnerText;
                                            HtmlAttribute clubURL = cell.SelectSingleNode("a").Attributes["href"];
                                            team1EspnId = clubURL.Value.GetTextBetween("team/_/id/", "/");
                                            team1Guid = ProcessTeam(team1EspnId, team1Name, (Guid)lookupCompetition.Competition.CountryGuid);
                                        }
                                        break;

                                    case 2:
                                        if (fullTime)
                                        {

                                            matchURL = cell.SelectSingleNode("a").Attributes["href"].Value;

                                            if (matchURL.IndexOf("/uk/en/report/") > -1)
                                                espnMatchId = matchURL.GetTextBetween("/uk/en/report/", "/report.html");
                                            else if (matchURL.IndexOf("/uk/en/gamecast/") > -1)
                                                espnMatchId = matchURL.GetTextBetween("/uk/en/gamecast/", "/gamecast.html");

                                            team1FT = Convert.ToInt16(cell.InnerText.GetTextBefore("-"));
                                            team2FT = Convert.ToInt16(cell.InnerText.GetTextAfter("-"));
                                        }
                                        break;

                                    case 3:

                                        if (cell.SelectSingleNode("a") != null)
                                        {
                                            team2Name = cell.InnerText;
                                            HtmlAttribute clubURL = cell.SelectSingleNode("a").Attributes["href"];
                                            team2EspnId = clubURL.Value.GetTextBetween("team/_/id/", "/");
                                            team2Guid = ProcessTeam(team2EspnId, team2Name, (Guid)lookupCompetition.Competition.CountryGuid);
                                        }
                                        break;
                                }
                            }

                            Guid? matchGuid = ProcessMatch(espnMatchId, ImportStatus.Result, matchDate, null, lookupCompetition.CompetitionGuid, team1Guid, null, team1FT, team2Guid, null, team2FT);
                        }
                    }
                }
            }
        }

        public void ImportMatchDetail(Guid matchGuid)
        {
            string espnId = Provider.LookupMatchRepository.Where(m => m.MatchGuid == matchGuid).Single().SiteMatchId;

            Match match = Provider.MatchRepository.Where(m => m.Pk == matchGuid).Single();

            match.Team1HT = 0;
            match.Team2HT = 0;

            Uri uri = new Uri(string.Format("http://espnfc.com/uk/en/gamecast/statistics/id/{0}/statistics.html?soccernet=true&cc=5739", espnId));
            HtmlDocument document = GetHtmlDocument(uri);

            string matchDateString = document.DocumentNode.SelectNodes(@"//div[@class=""match-details""]/p")[0].InnerText;
            long jQueryDate = Convert.ToInt64(matchDateString.GetTextBetween("new Date(", ");"));
            DateTime matchDate = new DateTime(1970, 1, 1, 0, 0, 0).AddMilliseconds(jQueryDate);

            string locationString = document.DocumentNode.SelectNodes(@"//div[@class=""match-details""]/p")[1].InnerText;
            Guid venueGuid = ProcessVenue(locationString, match.Competition.CountryGuid);

            foreach (HtmlNode header in document.DocumentNode.SelectNodes(@"//div[@class=""matchup""]"))
            {
                long attendance = Convert.ToInt64(header.SelectSingleNode("p/span").InnerText.Replace("Attendance:", string.Empty).Trim());

                Guid teamGuid = match.Team1Guid;

                foreach (HtmlNode table in document.DocumentNode.SelectNodes("//table[@class=\"stat-table\"]"))
                {
                    bool isSubstitute = false;

                    foreach (HtmlNode row in table.SelectNodes("tbody/tr"))
                    {
                        if (row.SelectSingleNode("th") != null)
                        {
                            if (row.SelectSingleNode("th").Attributes["class"].Value == "tb-substitutes home-team" || row.SelectSingleNode("th").Attributes["class"].Value == "tb-substitutes away-team")
                                isSubstitute = true;
                        }

                        if (row.SelectSingleNode("td") == null || row.SelectNodes("td").Count < 2)
                            continue;

                        string position = row.SelectNodes("td")[0].InnerText;
                        string fullName = row.SelectNodes("td")[2].SelectSingleNode("a").InnerText.Trim();

                        string forenames;
                        string surname;
                        ResolvePersonNames(fullName, out forenames, out surname);

                        string working = row.SelectNodes("td")[2].SelectSingleNode("a").Attributes["href"].Value;
                        string espnPersonId = working.GetTextBetween("player/_/id/", "/");

                        Guid personGuid = ProcessPerson(espnPersonId, forenames, surname, null, null);

                        HtmlNodeCollection appearanceAttributes = row.SelectNodes("td")[2].SelectNodes("div");
                        MatchEventType startingStatus = isSubstitute ? MatchEventType.Substitute : MatchEventType.Started;

                        Provider.MatchEventRepository.Add(new MatchEvent()
                        {
                            Pk = Guid.NewGuid(),
                            ApprovalGuid = Guid.NewGuid(),
                            MatchGuid = matchGuid,
                            TeamGuid = teamGuid,
                            PersonGuid = personGuid,
                            MatchEventType = startingStatus,
                            Minute = 0,
                            Extra = null,
                            IsApproved = User.IsApprover(),
                            AuditUserId = User.GetUserId(),
                            AuditTimestamp = DateTime.Now
                        });

                        Provider.SaveChanges();

                        if (appearanceAttributes != null)
                        {
                            foreach (HtmlNode appearanceAttribute in appearanceAttributes)
                            {
                                short? minute = null;
                                short? plus = null;
                                MatchEventType? matchEventType = GetMatchEvent(appearanceAttribute, out minute, out plus);

                                if (matchEventType != null)
                                {
                                    Provider.MatchEventRepository.Add(new MatchEvent()
                                    {
                                        Pk = Guid.NewGuid(),
                                        ApprovalGuid = Guid.NewGuid(),
                                        MatchGuid = matchGuid,
                                        TeamGuid = teamGuid,
                                        PersonGuid = personGuid,
                                        MatchEventType = (MatchEventType)matchEventType,
                                        Minute = (short)minute,
                                        Extra = plus,
                                        IsApproved = User.IsApprover(),
                                        AuditUserId = User.GetUserId(),
                                        AuditTimestamp = DateTime.Now
                                    });

                                    Provider.SaveChanges();
                                }

                                if (minute <= 45)
                                {
                                    if (matchEventType == MatchEventType.Scored && teamGuid == match.Team1Guid || matchEventType == MatchEventType.OwnGoal && teamGuid == match.Team2Guid)
                                        match.Team1HT++;

                                    if (matchEventType == MatchEventType.Scored && teamGuid == match.Team2Guid || matchEventType == MatchEventType.OwnGoal && teamGuid == match.Team1Guid)
                                        match.Team2HT++;
                                }
                            }
                        }
                    }

                    teamGuid = match.Team2Guid;
                }
            }

            ProcessMatch(espnId, ImportStatus.FullDetails, matchDate, venueGuid, match.CompetitionGuid, match.Team1Guid, null, match.Team1FT, match.Team2Guid, null, match.Team2FT);
        }

        private Guid ProcessVenue(string locationString, Guid? defaultCountryGuid)
        {
            locationString = locationString.Replace("\r", string.Empty).Replace("\n", string.Empty).Replace("\t", string.Empty);
            string venueName = (locationString.IndexOf(",") != -1) ? locationString.Substring(0, locationString.IndexOf(",")).Trim() : locationString.Trim();
            string countryName = (locationString.IndexOf(",") != -1) ? locationString.Substring(locationString.IndexOf(",") + 2) : string.Empty;

            Guid? countryGuid;

            if (countryName.Length > 0)
            {
                var countries = from c in Provider.CountryRepository.Where(c => c.CountryName == countryName)
                                select c;

                if (countries.Any())
                    countryGuid = countries.FirstOrDefault().Pk;
                else
                {
                    countryGuid = Guid.NewGuid();

                    Provider.CountryRepository.Add(new Country()
                    {
                        Pk = (Guid)countryGuid,
                        CountryName = countryName,
                        ActiveFrom = _DateTime.LowDate,
                        ActiveTo = _DateTime.HighDate,
                        IsApproved = User.IsApprover(),
                        AuditUserId = User.GetUserId(),
                        AuditTimestamp = DateTime.Now
                    });

                    Provider.SaveChanges();
                }
            }
            else
                countryGuid = defaultCountryGuid;

            var venues = from v in Provider.VenueRepository.Where(v => v.VenueName == venueName && v.CountryGuid == countryGuid)
                         select v;

            if (venues.Any())
                return venues.FirstOrDefault().Pk;
            else
            {
                Guid venueGuid = Guid.NewGuid();

                Provider.VenueRepository.Add(new Venue()
                {
                    Pk = venueGuid,
                    ApprovalGuid = Guid.NewGuid(),
                    VenueName = venueName,
                    Address = null,
                    CountryGuid = (Guid)countryGuid,
                    Capacity = null,
                    IsApproved = User.IsApprover(),
                    AuditUserId = User.GetUserId(),
                    AuditTimestamp = DateTime.Now
                });

                Provider.SaveChanges();

                return venueGuid;
            }
        }

        private static void ResolvePersonNames(string fullName, out string forenames, out string surname)
        {
            forenames = string.Empty;
            surname = string.Empty;

            if (fullName.IndexOf(" ") > 0)
            {
                for (int pos = fullName.Length - 1; pos > 0; pos--)
                {
                    if (fullName.Substring(pos, 1) == " ")
                    {
                        forenames = fullName.Substring(0, pos).Trim();
                        surname = fullName.Substring(pos).Trim();
                        break;
                    }
                }
            }
            else
                surname = fullName;
        }

        private static MatchEventType? GetMatchEvent(HtmlNode appearanceAttribute, out short? minute, out short? plus)
        {
            minute = null;
            plus = null;
            string working = appearanceAttribute.Attributes["onmouseover"].Value.GetTextBetween("</strong> - ", "\\'").Trim();
            int tryNumber;

            if (int.TryParse(working, out tryNumber))
                minute = (short)tryNumber;
            else
            {
                int plusPosition = working.IndexOf("+");

                if (plusPosition > 0)
                {
                    string lhs = working.Substring(0, plusPosition).Trim();

                    if (int.TryParse(lhs, out tryNumber))
                        minute = (short)tryNumber;

                    string rhs = working.Substring(plusPosition + 1);

                    if (int.TryParse(rhs, out tryNumber))
                        plus = (short)tryNumber;
                }
            }

            switch (appearanceAttribute.Attributes["class"].Value)
            {
                case "soccer-icons soccer-icons-redcard":
                    return MatchEventType.SentOff;

                case "soccer-icons soccer-icons-yellowcard":
                    return MatchEventType.Booked;

                case "soccer-icons soccer-icons-goal":
                    return MatchEventType.Scored;

                case "soccer-icons soccer-icons-owngoal":
                    return MatchEventType.OwnGoal;

                case "soccer-icons soccer-icons-subinout":
                    return MatchEventType.BroughtOn;

                default:
                    break;
            }

            return null;
        }
    }        
}